<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>浏览器坐标系属性</title>
    <link rel="stylesheet" href="https://www.linyannova.top/static/reset.css">
    <style>
      body {
        margin: 0;
        padding: 0;
        display: flex;
        height: 100vh;
        width: 100vw;
      }
      .div {
        text-align: center;
        font-size: 30px;
        height: 400px;
        line-height: 400px;
        width: 100%;
        color: yellow;
      }
      .div1 {
        background-color: red;
        position: relative;
        margin: 20px;
        border: 10px solid #000;
      }

      .div2 {
        background-color: green;
        height: 200px;
        overflow: auto;
      }
      .div3 {
        background-color: blue;
        border: 10px solid #000;
        position: relative;
      }
      .div3 .item {
        position: absolute;
        left: 100px;
        top: 20px;
        width: 50px;
        height: 50px;
        background-color: green;
        white-space: nowrap;
        line-height: 50px;
        font-size: 16px;
      }
      .div4 {
        background-color: #eee;
        line-height: 200px;
        overflow: auto;
      }
      .tip {
        position: fixed;
        background-color: yellow;
        top: 0;
      }
      .display, .discription {
        height: 100%;
        overflow-y: auto;
        flex: 1 1 50%;
      }
      .red {
        color: red;
      }
      h2:has(+ button) {
        display: inline-block;
        margin-right: 10px;
      }
      p:has(> span:is(.red)) {
        margin: 10px 0;
      }
    </style>
  </head>
  <body>
    <!-- 演示区域 -->
    <div class="display">
      <div class="div div1">client属性展示区域</div>
      <div class="div div2">
        scroll属性展示区域
      </div>
      <div class="div div3">
        offset属性
        <div class="item">子div</div>
      </div>
      <div id="tip" class="tip">
      </div>
    </div>
    <!-- 概念描述区域 -->
    <div class="discription">
      <h2>Screen系列</h2><button id="screen">悬浮框显示我</button>
      <p><span class="red">screenX</span>: 鼠标位置相对于用户屏幕水平偏移量</p>
      <p><span class="red">screenY</span>: 鼠标位置相对于用户屏幕垂直偏移量</p>
      <h2>client系列</h2><button id="client">悬浮框显示我</button>
      <p><span class="red">clientX</span>: 鼠标位置相对于文档的左边距（不随页面滚动而改变）</p>
      <p><span class="red">clientY</span>: 鼠标位置相对于文档的上边距（不随页面滚动而改变）</p>
      <p><span class="red">clientHeight</span>: 表示元素节点的 CSS 高度（单位像素），只对块级元素生效，对于行内元素返回0。如果块级元素没有设置 CSS 高度，则返回实际高度</p>
      <p>clientHeight = height + padding</p>
      <p><span class="red">clientWidth</span>: 表示元素节点的 CSS 宽度（单位像素），只对块级元素有效，对于行内元素返回0。也是只包括元素本身的宽度和padding，如果有垂直滚动条，还要减去垂直滚动条的宽度</p>
      <p>clientWidth = width + padding</p>
      <p><span class="red">clientLeft</span>: 元素节点左边框（left border）的宽度（单位像素），不包括左侧的padding和margin。如果没有设置左边框，或者是行内元素（display: inline），该属性返回0</p>
      <p><span class="red">clientTop</span>: 元素节点顶部边框的宽度（单位像素），不包括顶部的padding和margin。如果没有设置上边框，或者是行内元素（display: inline），该属性返回0</p>
      <h2>scroll系列</h2><button id="scroll">悬浮框显示我</button>
      <p><span class="red">scrollWidth</span>: 获取元素整个内容的宽度 （包含看不见的） ，如果有滚动条（滚动条会占用部分宽高），不计算滚动条的宽高</p>
      <p><span class="red">scrollHeight</span>: 获取元素整个内容的高度 （包含看不见的） ，如果有滚动条（滚动条会占用部分宽高），不计算滚动条的宽高</p>
      <p><span class="red">scrollTop(可读写)</span>: 表示当前元素的垂直滚动条向下侧滚动的像素数量</p>
      <p><span class="red">scrollLeft(可读写)</span>: 表示当前元素的滚动水平滚动条向右的像素数量</p>
      <p>判断滚动条垂直是否滚动到底 垂直滚动条 scrollHeight -scrollTop = clientHeight</p>
      <p>判断滚动条水平是否滚动到底 水平滚动条 scrollWidth -scrollLeft = clientWidth</p>
      <h2>offset系列</h2><button id="offset">悬浮框显示我</button>
      <p><span class="red">offsetX</span>: 鼠标位置相对被触发dom的左上角的水平距离，以内容区左上角为基准点（不包括边框），如果触发点在边框上会返回负值</p>
      <p><span class="red">offsetY</span>: 鼠标位置相对被触发dom的左上角的垂直距离，以内容区左上角为基准点（不包括边框），如果触发点在边框上会返回负值</p>
      <p><span class="red">offsetHeight</span>: 表示元素的 CSS 垂直高度（单位像素），包括元素本身的高度、padding 和 border，以及水平滚动条的高度（如果存在滚动条）, 如果元素的 CSS 设为不可见（比如display: none;），则返回0</p>
      <p>offsetHeight = height + border + padding</p>
      <p><span class="red">offsetWidth</span>: 表示元素的 CSS 水平宽度（单位像素），包括元素本身的宽度、padding 和 border，以及垂直滚动条的宽度（如果存在滚动条）, 如果元素的 CSS 设为不可见（比如display: none;），则返回0</p>
      <p>offsetWidth = width + border + padding</p>
      <p><span class="red">offsetParent</span>: 返回最靠近当前元素的、并且 CSS 的position属性不等于static的上层元素</p>
      <p><span class="red">offsetLeft</span>: 当前元素和定位父元素之间的偏移量（如果没有设置定位父元素，就是相对于左上角的位置,left是最左边的距离） </p>
      <p><span class="red">offsetTop</span>: 当前元素和定位父元素之间的偏移量（如果没有设置定位父元素，就是相对于左上角的位置,top是距离顶部的距离）</p>
      <h2>其它</h2><button id="other">悬浮框显示我</button>
      <p><span class="red">注意：当浏览器body出现滚动条时，pageY和clientY才不一致！</span></p>
      <p><span class="red">pageX</span>: 鼠标位置相对于文档的左边距（随页面滚动而改变）</p>
      <p>e.pageX = e.clientX + e.target.scrollLeft;</p>
      <p><span class="red">pageY</span>: 鼠标位置相对于文档的上边距（随页面滚动而改变）</p>
      <p>e.pageY = e.clientY + e.target.scrollTop;</p>
      <p><span class="red">layerX</span>: 有定位属性的父元素的左上角的水平距离（自身有定位属性的话就是相对于自身），都没有的话，就是相对于body的左上角（包含边框）</p>
      <p><span class="red">layerY</span>: 有定位属性的父元素的左上角的垂直距离（自身有定位属性的话就是相对于自身），都没有的话，就是相对于body的左上角（包含边框）</p>
      <p><span class="red">getBoundingClientRect</span>: 用于获得页面中某个元素的左，上，右和下分别相对浏览器视窗(可视范围)的位置</p>
      <p>getBoundingClientRect方法的所有属性，都把边框（border属性）算作元素的一部分</p>
      <ul>  
        <li>x：元素左上角相对于视口的横坐标</li>
        <li>y：元素左上角相对于视口的纵坐标</li>
        <li>height：元素高度</li>
        <li>width：元素宽度</li>
        <li>left：元素左上角相对于视口的横坐标，与x属性相等</li>
        <li>right：元素右边界相对于左边视口的横坐标（等于x + width）</li>
        <li>top：元素顶部相对于视口的纵坐标，与y属性相等</li>
        <li>bottom：元素底部相对于上边视口的纵坐标（等于y + height）</li>
      </ul>
    </div>
    <script>
      const tip = document.querySelector('#tip');
      const buttons = document.querySelectorAll('button')
      buttons.forEach(button => {
        button.addEventListener('click', (e) => {
          // 使用 e.target 判断是哪个按钮被点击
          if (e.target.tagName === 'BUTTON') {
            mouseMoveInit(e.target.id)
          }
        });
      });
      const mouseMoveInit = (id) => {
        document.onmousemove = null;
        switch(id) {
          case 'screen': {
            document.onmousemove = function (e) {
              if (e == null) {
                e = window.event;
              }
              let html = ''
              html = "screenX:" + e.screenX + "<br/>";
              html += "screenY:" + e.screenY + "<br/>";
              tip.innerHTML = html;
            }
            break;
          }
          case 'client': {
            document.onmousemove = function (e) {
              if (e == null) {
                e = window.event;
              }
              let html = ''
              html += "clientX:" + e.clientX + "<br/>";
              html += "clientY:" + e.clientY + "<br/>";
              html += "clientHeight:" + e.target.clientHeight + "<br/>";
              html += "clientWidth:" + e.target.clientWidth + "<br/>";
              html += "clientLeft:" + e.target.clientLeft + "<br/>";
              html += "clientTop:" + e.target.clientTop + "<br/>";
              tip.innerHTML = html;
            }
            break;
          }
          case 'scroll': {
            document.onmousemove = function (e) {
              if (e == null) {
                e = window.event;
              }
              let html = ''
              html += "scrollWidth:" + e.target.scrollWidth + "<br/>";
              html += "scrollHeight:" + e.target.scrollHeight + "<br/>";
              html += "scrollTop:" + e.target.scrollTop + "<br/>";
              html += "scrollLeft:" + e.target.scrollLeft + "<br/>";
              tip.innerHTML = html;
            }
            break;
          }
          case 'offset': {
            document.onmousemove = function (e) {
              if (e == null) {
                e = window.event;
              }
              let html = ''
              html += "offsetX:" + e.offsetX + "<br/>";
              html += "offsetY:" + e.offsetY + "<br/>";
              html += "offsetWidth:" + e.target.offsetWidth + "<br/>";
              html += "offsetHeight:" + e.target.offsetHeight + "<br/>";
              html += "offsetLeft:" + e.target.offsetLeft + "<br/>";
              html += "offsetTop:" + e.target.offsetTop + "<br/>";
              html += "offsetParent:" + e.target.offsetParent + "<br />";
              tip.innerHTML = html;
            }
            break;
          }
          case 'other': {
            document.onclick = function (e) {
              if (e == null) {
                e = window.event;
              }
              let html = ''
              html += "pageX:" + e.pageX + ',' + e.clientX + ',' +e.target.scrollLeft + "<br/>";
              html += "pageY:" + e.pageY + ',' + e.clientY + ',' +e.target.scrollTop + "<br/>";
              html += "layerX:" + e.layerX + "<br/>";
              html += "layerY:" + e.layerY + "<br/>";
              tip.innerHTML = html;
            }
            break;
          }
        }
      }
      // 默认初始化screen
      mouseMoveInit('screen');
    </script>
  </body>
</html>
